﻿ #region Disclaimer/Info
 
 /////////////////////////////////////////////////////////////////////////////////////////////////
 //
 //   File:		SiteMapHandler.cs
 //   Website:		http://dexterblogengine.com/
 //   Authors:		http://dexterblogengine.com/About.ashx
 //   Rev:		1
 //   Created:		19/01/2011
 //   Last edit:		19/01/2011
 //   License:		GNU Library General Public License (LGPL)
 // 
 //   For updated news and information please visit http://dexterblogengine.com/
 //   Dexter is hosted to Codeplex at http://dexterblogengine.codeplex.com
 //   For any question contact info@dexterblogengine.com
 //
 ///////////////////////////////////////////////////////////////////////////////////////////////////
 
 #endregion

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Web;
using System.Xml.Linq;
using Dexter.Web.HttpHandlers;
using Dexter.DataTransferObject;
using Dexter.Dependency;
using Dexter.Domain.Model;
using Dexter.Services;

namespace Dexter.Web.HttpHandlers {
	/// <summary>
	/// The <see cref="IHttpHandler"/> for SEO Site map generation.
	/// </summary>
	public class SiteMapHandler : HttpHandlerBase {
		readonly ICategoryService categoryService;
		readonly IPageService pageService;
		readonly IPostService postService;
		readonly ITagService tagService;
		readonly IUrlBuilder urlBuilder;
		readonly XNamespace xmlns = "http://www.sitemaps.org/schemas/sitemap/0.9";
		readonly XNamespace xsi = "http://www.w3.org/2001/XMLSchema-instance";

		/// <summary>
		/// Initializes a new instance of the <see cref="SiteMapHandler"/> class.
		/// </summary>
		public SiteMapHandler ( ) {
			postService = DexterContainer.Resolve <IPostService> ( );
			pageService = DexterContainer.Resolve <IPageService> ( );
			urlBuilder = DexterContainer.Resolve <IUrlBuilder> ( );
			tagService = DexterContainer.Resolve <ITagService> ( );
			categoryService = DexterContainer.Resolve <ICategoryService> ( );
		}

		/// <summary>
		/// Initializes a new instance of the <see cref="SiteMapHandler"/> class.
		/// </summary>
		/// <param name="postService">The post service.</param>
		/// <param name="pageService">The page service.</param>
		/// <param name="urlBuilder">The URL builder.</param>
		/// <param name="tagService">The tag service.</param>
		/// <param name="categoryService">The category service.</param>
		public SiteMapHandler ( IPostService postService , IPageService pageService , IUrlBuilder urlBuilder , ITagService tagService , ICategoryService categoryService ) {
			this.postService = postService;
			this.pageService = pageService;
			this.urlBuilder = urlBuilder;
			this.tagService = tagService;
			this.categoryService = categoryService;
		}

		public override void ProcessRequest ( HttpContextBase context ) {
			IList <Post> items = postService.GetPublished ( 0 , 10000 ).Result.ToList ( );
			IEnumerable <PageStructure> pages = GetAllPages ( );
			IEnumerable <TagDto> tags = tagService.GetTopTags ( 300 );

			XNamespace ns = "http://www.sitemaps.org/schemas/sitemap/0.9";
			XDocument sitemap = new XDocument ( new XDeclaration ( "1.0" , "utf-8" , "yes" ) );

			XElement urlSet = new XElement ( ns + "urlset" );
			sitemap.Add ( urlSet );

			urlSet.Add ( new XAttribute ( "xmlns" , "http://www.sitemaps.org/schemas/sitemap/0.9" ) );
			urlSet.Add ( new XAttribute ( XNamespace.Xmlns + "xsi" , "http://www.w3.org/2001/XMLSchema-instance" ) );
			urlSet.Add ( new XAttribute ( xsi + "schemaLocation" , "http://www.google.com/schemas/sitemap/0.84 http://www.sitemaps.org/schemas/sitemap/0.9/sitemap.xsd" ) );


			//Adding the root of the site
			urlSet.Add ( CreateNode ( urlBuilder.HomePage.ToString ( ) , items.Count > 0
			                                                             	? items[ 0 ].PublishDate
			                                                             	: DateTime.Today , "daily" , "1.00" ) );

			//Adding the posts
			foreach ( Post item in items ) {
				urlSet.Add ( CreateNode ( urlBuilder.Post.Detail ( item ) , item.PublishDate , GetPostChangeFrequency ( item ) , GetPostPriority ( item ) ) );
			}

			//Adding the pages
			foreach ( PageStructure page in pages ) {
				urlSet.Add ( CreateNode ( urlBuilder.Page.Detail ( page ) , null , null , GetPagetPriority ( page ) ) );
			}

			//Adding the posts
			if ( tags != null ) {
				foreach ( TagDto tag in tags ) {
					urlSet.Add ( CreateNode ( urlBuilder.Tag.Detail ( tag.Name ) , null , null , "0.50" ) );
				}
			}

			var categories = categoryService.GetCategoriesStructure ( ).ToFlat ( x => x.Categories );

			//Adding the categories)
			if ( categories != null ) {
				foreach ( var category in categories ) {
					urlSet.Add ( CreateNode ( urlBuilder.Category.Detail ( category ) , null , null , "0.50" ) );
				}
			}

			var archives = postService.GetMonths ( );

			//Adding the monthly archives
			if ( archives != null ) {
				foreach ( var month in archives ) {
					urlSet.Add ( CreateNode ( urlBuilder.Post.ArchivePosts ( month.MonthID , month.Year ) , null , null , "0.50" ) );
				}
			}

			sitemap.Save ( context.Response.Output );
			context.Response.ContentEncoding = Encoding.UTF8;
			context.Response.ContentType = "text/xml";
			context.Response.Charset = "UTF-8";
			context.Response.Cache.SetValidUntilExpires ( true );
			context.Response.Cache.SetExpires ( DateTime.Now.AddDays ( 3 ) );
			context.Response.Cache.SetETag ( Guid.NewGuid ( ).ToString ( ) );
		}

		XElement CreateNode ( string url , DateTime? lastModifyDate , string changeFreq , string priority ) {
			XElement root = new XElement ( xmlns + "url" );
			XElement loc = new XElement ( xmlns + "loc" , url );

			root.Add ( loc );

			if ( lastModifyDate.HasValue ) {
				XElement lastmod = new XElement ( xmlns + "lastmod" , lastModifyDate.Value.ToString ( "yyyy-MM-dd" ) );
				root.Add ( lastmod );
			}

			if ( !string.IsNullOrEmpty ( changeFreq ) ) {
				XElement chFreq = new XElement ( xmlns + "changefreq" , changeFreq );
				root.Add ( chFreq );
			}

			if ( !string.IsNullOrEmpty ( priority ) ) {
				XElement pr = new XElement ( xmlns + "priority" , priority );
				root.Add ( pr );
			}

			return root;
		}

		IEnumerable <PageStructure> GetAllPages ( ) {
			IList <PageStructure> dynamicPages = pageService.GetPagesStructure ( ).ToList ( );

			dynamicPages.Add ( new PageStructure {
				Id = -1 ,
				Title = "Blog" ,
				Slug = "Blog" ,
				IsPublished = true
			} );

			dynamicPages.Add ( new PageStructure {
				Id = -2 ,
				Title = "Tags" ,
				Slug = "Tags" ,
				IsPublished = true
			} );
			dynamicPages.Add ( new PageStructure {
				Id = -3 ,
				Title = "Contributes" ,
				Slug = "Contributes" ,
				IsPublished = true
			} );
			dynamicPages.Add ( new PageStructure {
				Id = -4 ,
				Title = "Contacts" ,
				Slug = "Contacts" ,
				IsPublished = true
			} );

			return dynamicPages;
		}

		static string GetPagetPriority ( PageStructure item ) {
			if ( item.Pages != null && item.Pages.Count > 0 ) {
				return "1.00";
			}

			return "0.50";
		}

		static string GetPostPriority ( Item item ) {
			if ( !item.CommentEnabled ) {
				return "0.50";
			}

			bool isOldPost = item.PublishDate.ToUniversalTime ( ).Subtract ( DateTime.Now.ToUniversalTime ( ) ).Days > 60;

			if ( isOldPost ) {
				return "1.00";
			}

			return "0.50";
		}

		static string GetPostChangeFrequency ( Item item ) {
			if ( !item.CommentEnabled ) {
				return "yearly";
			}

			int days = DateTime.Now.ToUniversalTime ( ).Subtract ( item.PublishDate.ToUniversalTime ( ) ).Days;

			if ( days <= 7 ) {
				return "daily";
			}

			if ( days <= 30 ) {
				return "daily";
			}

			if ( days <= 60 ) {
				return "monthly";
			}

			if ( days <= 180 ) {
				return "yearly";
			}

			return "monthly";
		}
	}
}
